/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include "bootloader.h"
#include "AT32Models.h"

#define CRC32_INIT_VALUE 0xFFFFFFFF
#define CRC32_XOR_VALUE 0xFFFFFFFF

uint32_t crc32_cal(const uint8_t *pdata, int32_t len, uint32_t initial, uint32_t finalXor, bool inputReflected, bool resultReflected);

firmware_info_t firmware;
bl_info_t bootloader;
bl_info_t *bl = &bootloader;
uint8_t *firmware_data = NULL;
int f_com = 0;
uint16_t SetSize = 0;

uint16_t bl_write(uint8_t *buff, uint16_t len)
{
    if (f_com == 0)
    {
        printf("bl_write error, f_com: 0x%08X\n", f_com);
        return 0;
    }
    uint16_t ret = write(f_com, buff, len);
    // tcflush(f_com, TCIOFLUSH);
    if (ret != len)
        printf("bl_write error, ret:%d\n", ret);
    return ret;
}
uint16_t bl_read(uint8_t *buff)
{
    if (!f_com)
    {
        printf("bl_read error, f_com: 0x%08X\n", f_com);
        return 0;
    }

    uint16_t len = 0;
    for (;;)
    {
        if (read(f_com, buff, 1) == 1)
        {
            buff++;
            len++;
        }
        else
        {
            return len;
        }
    }
    return len;
}
int firmware_extract(FILE *f, firmware_info_t *i)
{
    if (!f)
        return -1;
    size_t readsize = fread(i, 1, sizeof(firmware_info_t), f);
    if (readsize != sizeof(firmware_info_t))
    {
        printf("firmware_extract error, readsize:%d\n", readsize);
        return -1;
    }

    // printf("readsize:%d\n",readsize);
    uint32_t crc = crc32_cal((const uint8_t *)i + 4, sizeof(firmware_info_t) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    if (crc != i->infoCrc)
    {
        printf("firmware_extract error, crc: 0x%08X, infoCrc: 0x%08X\n", crc, i->infoCrc);
        return -1;
    }
    printf(" *name     : %s\n", i->name);
    printf(" *infoCrc  : 0x%08X\n", i->infoCrc);
    printf(" *magic    : 0x%08X\n", i->magic);
    printf(" *startAddr: 0x%08X\n", i->start_addr);
    printf(" *size     : 0x%08X\n", i->size);
    printf(" *FWcrc    : 0x%08X\n", i->FWcrc);
    printf(" *version  : 0x%08X\n", i->version);
    printf(" *pid      : 0x%08X\n", i->pid);
    printf(" *date     : 0x%08X\n", i->date);
    printf(" *Serial   : 0x%08X\n", i->whoAmI.Serial);
    printf(" *ID       : 0x%08X\n", i->whoAmI.ID);
    printf(" *Model    : 0x%08X\n", i->whoAmI.Model);
    printf(" *Flash    : 0x%08X\n", i->whoAmI.Flash);
    printf(" *Footprint: 0x%08X\n", i->whoAmI.Footprint);

    firmware_data = (uint8_t *)malloc(i->size);
    readsize = fread(firmware_data, 1, i->size, f);

    // FILE *fw = fopen("./write.bin", "wb");
    // fwrite(firmware_data, 1, i->size, fw);
    // fclose(fw);

    if (readsize != i->size)
    {
        printf("firmware_extract error, readsize:%d/%d\n", readsize, i->size);
        return -1;
    }
    crc = crc32_cal(firmware_data, i->size, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    if (crc != i->FWcrc)
    {
        printf("firmware_extract error, crc:%x, FWcrc:%x\n", crc, i->FWcrc);
        return -1;
    }
    printf("FWcrc: 0x%08X\n", crc);
    return 0;
}
void delay_ms(uint32_t ms)
{
    usleep(ms * 1000);
}
int serial_init(int *fcom, char *com, int baudrate)
{
    /**get com num */

    char linuxCom[12] = "/dev/ttyS";
    int comIndex = -1;

    if (strncmp(com, "COM", 3) == 0)
    {
        comIndex = atoi(com + 3) - 1;
        if (comIndex < 9)
            linuxCom[9] = comIndex + '0';
        else if (comIndex < 100)
        {
            linuxCom[9] = comIndex / 10 + '0';
            linuxCom[10] = comIndex % 10 + '0';
        }
        else
        {
            printf("com error!\n");
            return -1;
        }
        printf("\ncomIndex = %d\n", comIndex);
        printf("com = %s\n", linuxCom);
    }
    else
    {
        printf("com error!\n");
        return -1;
    }

    /**get baudrate */
    printf("baudrate:%d \n", baudrate);
    /**open and init com port*/
    *fcom = open(linuxCom, O_RDWR | O_NOCTTY | O_NDELAY);
    if (*fcom == -1)
    {
        printf("com port open error! %s\n", strerror(errno));
        return -1;
    }
    printf("serial open\n");

    struct termios options;
    tcgetattr(*fcom, &options);
    switch (baudrate)
    {
    case 9600:
        cfsetispeed(&options, B9600); // 设置输入波特率为921600
        cfsetospeed(&options, B9600); // 设置输出波特率为921600
        break;
    case 115200:
        cfsetispeed(&options, B115200); // 设置输入波特率为921600
        cfsetospeed(&options, B115200); // 设置输出波特率为921600
        break;
    case 921600:
        cfsetispeed(&options, B921600); // 设置输入波特率为921600
        cfsetospeed(&options, B921600); // 设置输出波特率为921600
        break;

    default:

        cfsetispeed(&options, B921600); // 设置输入波特率为921600
        cfsetospeed(&options, B921600); // 设置输出波特率为921600
        break;
    }
    options.c_cflag |= (CLOCAL | CREAD);                // 忽略调制解调器控制线，启用接收器
    options.c_cflag &= ~PARENB;                         // 无奇偶校验
    options.c_cflag &= ~CSTOPB;                         // 1位停止位
    options.c_cflag &= ~CSIZE;                          // 清除数据位设置
    options.c_cflag |= CS8;                             // 设置数据位为8位
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 非规范模式，禁止
    options.c_oflag &= ~OPOST;                          // 原始输出
    options.c_cc[VTIME] = 1;                            // 设置超时1
    options.c_cc[VMIN] = 1;                             // 设置最小字符为1
    tcsetattr(*fcom, TCSANOW, &options);

    printf("serial init done\n");
    return 0;
}
int dev_sync(bl_info_t *bl)
{
    int sendtry = 0;
    printf("\nTry to sync");
    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_SYNC, 0, NULL, 0);
        // printf("\nsending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");
        delay_ms(1);
        if (bl_pack_recv(bl) == 0 && bl->pack.cmd1 == CMD_ACK)
        {
            printf("Sync success!\n");
            // break;
            return 0;
        }
        else
        {
            delay_ms(10);
            sendtry++;
            if (sendtry >= 300)
            {
                printf("sync timeout! exit!\n");
                return -2;
            }
        }
        printf("\nrev: %d", bl->pack.buffer_size);
        for (int i = 0; i < bl->pack.buffer_size; i++)
        {
            if (i % 16 == 0)
                printf("\n");
            printf("%02X ", bl->pack.buffer[i]);
        }
        printf("\n");
    }
    printf("\n");
    return -1;
}
int getVersion(bl_info_t *bl)
{
    printf("\nget version\n");
    int sendtry = 0;
    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_VERSION, 0, NULL, 0);
        // printf("\nsending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");
        delay_ms(1);
        if (bl_pack_recv(bl) == 0 && bl->pack.cmd1 == CMD_ACK)
        {
            if (bl->pack.cmd1 == CMD_ACK)
            {
                printf("get version success\n");
                memcpy(&bl->version, bl->pack.data, bl->pack.data_size);
                printf("version:0x%08X\n", bl->version);
                return 0;
            }
        }
        delay_ms(1);
        if (sendtry++ >= 3)
        {
            printf("get version timeout! exit!\n");
            return -1;
        }
        printf(".");
    }
    printf("\n");
}
int getId(bl_info_t *bl)
{
    printf("\nget id...\n");
    int sendtry = 0;
    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_GETID, 0, NULL, 0);
        // printf("\nsending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");
        delay_ms(1);
        if (bl_pack_recv(bl) == 0 && bl->pack.cmd1 == CMD_ACK)
        {
            if (bl->pack.cmd1 == CMD_ACK)
            {
                printf("get id success\n");
                memcpy(&bl->whoAmI, bl->pack.data, bl->pack.data_size);
                printf(" *Serial   : 0x%08X\n", bl->whoAmI.Serial);
                printf(" *ID       : 0x%08X\n", bl->whoAmI.ID);
                printf(" *Model    : 0x%08X\n", bl->whoAmI.Model);
                printf(" *Flash    : 0x%08X\n", bl->whoAmI.Flash);
                printf(" *Footprint: 0x%08X\n", bl->whoAmI.Footprint);
                printf(" *UID      : ");
                for (int i = 0; i < sizeof(bl->whoAmI.UID); i++)
                {
                    printf("%02X", bl->whoAmI.UID[i]);
                }
                printf("\n");
                int mcuMatch = 0;
                if (bl->whoAmI.Serial != firmware.whoAmI.Serial)
                {
                    printf("whoAmI Serial not match!\n");
                    mcuMatch++;
                }
                if (bl->whoAmI.Model != firmware.whoAmI.Model)
                {
                    printf("whoAmI Model not match!\n");
                    mcuMatch++;
                }
                if (bl->whoAmI.Flash != firmware.whoAmI.Flash)
                {
                    printf("whoAmI Flash not match!\n");
                    mcuMatch++;
                }
                if (mcuMatch != 0)
                {
                    printf("MCU not match!\n");
                    return -1;
                }
                printf(" - - - MCU match! - - - \n\n\n");
                // memcpy(firmware.whoAmI.UID, bl->whoAmI.UID, sizeof(bl->whoAmI.UID));
                return 0;
            }
        }
        delay_ms(1);
        if (sendtry++ >= 3)
        {
            printf("get version timeout! exit!\n");
            return -1;
        }
        printf(".");
    }
    printf("\n\n");
    return -1;
}
int get_fwInfo(bl_info_t *bl)
{
    printf("get fwInfo...\n");
    int sendtry = 0;
    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_R_FW_INFO, SetSize, NULL, 0);
        // printf("\nSending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");
        delay_ms(10);
        uint32_t ret = bl_pack_recv(bl);
        if (ret == 0)
        {
            if (bl->pack.cmd1 == CMD_ACK && bl->pack.data_size == sizeof(firmware_info_t))
            {
                firmware_info_t onchipFW;

                printf("get fwInfo success:crc 0x%08X\n", bl->pack.crc);
                memcpy(&onchipFW, bl->pack.data, sizeof(firmware_info_t));
                uint32_t iCrc = crc32_cal((uint8_t *)&onchipFW + 4, sizeof(firmware_info_t) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
                if (iCrc == onchipFW.infoCrc)
                {
                    int b3, b2, b1, b0;
                    printf("get onchip fwInfo:\n");
                    printf("name: %s\n", onchipFW.name);
                    b3 = onchipFW.version >> 24;
                    b2 = (onchipFW.version >> 16) & 0xFF;
                    b1 = (onchipFW.version >> 8) & 0xFF;
                    b0 = onchipFW.version & 0xFF;
                    printf("version: V%d.%d.%d.%d\n", b3, b2, b1, b0);
                    b3 = onchipFW.pid >> 24;
                    b2 = (onchipFW.pid >> 16) & 0xFF;
                    b1 = (onchipFW.pid >> 8) & 0xFF;
                    b0 = onchipFW.pid & 0xFF;
                    printf("pid: %d,%d,%d,%d\n", b3, b2, b1, b0);
                    int y, m, d;
                    y = onchipFW.date >> 16;
                    m = (onchipFW.date >> 8) & 0xFF;
                    d = onchipFW.date & 0xFF;
                    printf("date: %d-%d-%d\n", y, m, d);
                    printf("bootTimes:%d\n\n", onchipFW.bootTimes);
                }
                else
                {
                    printf("iCrc error! 0x%08X,%08X\n", iCrc, onchipFW.infoCrc);
                    printf("onchip fw empty!\n");
                }
                // for(int i=0;i<bl->pack.data_size;i++)
                // {
                //     if(i%16==0)printf("\n");
                //     printf("%02X ",bl->pack.data[i]);
                // }
                return 0;
            }
        }
        printf("\n reving: %d,0x%08X\n", bl->pack.buffer_size, ret);
        for (int i = 0; i < bl->pack.buffer_size; i++)
        {
            if (i % 16 == 0)
                printf("\n");
            printf("%02X ", bl->pack.buffer[i]);
        }
        printf("\n");
        delay_ms(1);
        if (sendtry++ >= 1)
        {
            printf("get fwInfo timeout! exit!\n");
            return -1;
        }
        printf(".");
    }
    printf("\n");
    return -2;
}
int erase_flash(bl_info_t *bl)
{
    printf("\n\nerase flash...\n");
    int sendtry = 0;
    uint32_t erase_size = firmware.size;
    uint32_t erase_addr = firmware.start_addr;
    uint32_t sectorCount = erase_size / bl->sectorSize;
    sectorCount += ((erase_size % bl->sectorSize) ? 1 : 0);
    printf("erase addr: 0x%08X, erase size: %d, sector count: %d %d\n", erase_addr, erase_size, sectorCount, bl->sectorSize);

    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_ERASE, erase_addr, (uint8_t *)&erase_size, 4);

        // printf("Sending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");

        uint32_t endAddr = 0;
        int waittry = 0;

        printf("erasing...\n");
        for (int i = 0; i <= sectorCount; i++)
        {
            printf("=");
        }
        printf("\n");

        while (1)
        {
            delay_ms(10);
            uint32_t ret = bl_pack_recv(bl);
            if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
            {
                endAddr = bl->pack.data[0] | (bl->pack.data[1] << 8) | (bl->pack.data[2] << 16) | (bl->pack.data[3] << 24);

                printf("#");
                fflush(stdout);
                // delay_ms(1);
                // printf("erase addr: 0x%08X, endAddr: 0x%08X\n", bl->pack.addr, endAddr);
                if (bl->pack.addr >= endAddr)
                {

                    printf("\n");
                    for (int i = 0; i <= sectorCount; i++)
                    {
                        printf("=");
                    }
                    printf("\n");
                    printf("erase success!\n\n");
                    return 0;
                }
                waittry = 0;
            }
            else
            {
                if (waittry++ > 10)
                {
                    printf("eraser error at 0x%08X\n", bl->pack.addr);
                    return -1;
                }
            }
        }
        if (sendtry++ >= 2)
        {
            printf("erase timeout! exit!\n");
            return -2;
        }
    }
    return -3;
}
int write_firmware(bl_info_t *bl)
{
    // firmware_data
    uint32_t write_already = 0;
    uint32_t write_remain = firmware.size;
    uint32_t write_addr = firmware.start_addr;
    uint32_t write_size = 0;
    uint32_t sendtry = 0;

    uint32_t stepPercent = 0;
    uint32_t stepPercentPre = 0;
    uint32_t stepMax = (firmware.size) / bl->sectorSize;
    stepMax = stepMax > 100 ? 100 : stepMax;
    printf("programming...\n");
    for (int i = 0; i <= stepMax; i++)
    {
        printf("=");
    }
    printf("\n");
    while (1)
    {
        bl_pack_send(bl, CMD_WRITE, write_addr, &firmware_data[write_already], bl->sectorSize);
        int ret = bl_pack_recv(bl);
        int waitry = 0;
        while (ret != 0)
        {
            delay_ms(1);
            ret = bl_pack_recv(bl);
            if (waitry++ > 100)
            {
                printf("write error [%08X] at 0x%08X, crc error\n", ret, write_addr);
                return -1;
            }
        }
        delay_ms(1);
        if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
        {
            write_already += write_size;
            write_remain -= write_size;
            write_addr += write_size;
            write_size = write_remain > bl->sectorSize ? bl->sectorSize : write_remain;

            stepPercent = (write_already * 100) / firmware.size;
            if (stepPercent != stepPercentPre)
            {
                stepPercentPre = stepPercent;
                printf(">");
                // printf(">%d%%\n",stepPercent);
                fflush(stdout);
            }
            // printf("write addr: 0x%08X, write_size: %d\n", write_addr, write_size);

            if (write_remain == 0)
            {
                printf("\n");
                for (int i = 0; i <= stepMax; i++)
                {
                    printf("=");
                }
                printf("\n");
                printf("Done!\n\n");
                return 0;
            }
        }
        else
        {
            printf("write error [%08X] at 0x%08X\n rev: [%d] \n", ret, write_addr, bl->pack.buffer_size);
            for (int i = 0; i < bl->pack.buffer_size; i++)
            {
                printf("0x%02X ", bl->pack.buffer[i]);
            }
            printf("\n");
            if (sendtry++ >= 10)
            {
                printf("write timeout! exit!\n");
                return -1;
            }
        }
    }
    return -2;
}
int read_firmware(bl_info_t *bl)
{
    uint32_t read_total = 0;
    uint32_t read_size = 0;
    uint32_t sendtry = 0;
    uint32_t read_addr = firmware.start_addr;
    uint32_t read_remain = firmware.size;
    uint8_t *firmwareRead = malloc(firmware.size);
    while (1)
    {
        read_size = read_remain > bl->sectorSize ? bl->sectorSize : read_remain;
        bl_pack_send(bl, CMD_READ, read_addr, (uint8_t *)&read_size, 4);

        int waitting = 0;
        while (1) // wait ack
        {
            delay_ms(1);
            int ret = bl_pack_recv(bl);
            if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
            {
                memcpy(&firmwareRead[read_total], bl->pack.data, bl->pack.data_size);
                printf("read addr: 0x%08X, read_size: %d\n", read_addr, read_size);
                if (read_size != bl->pack.data_size)
                {
                    printf("read error at 0x%08X! wanted[%d] get[%d]\n", read_addr, read_size, bl->pack.data_size);
                }
                read_total += read_size;
                read_remain -= read_size;
                read_addr += read_size;
                sendtry = 0;
                break;
            }
            else
            {
                printf("rev: 0x%08X, [%d]", ret, bl->pack.buffer_size);
                for (int i = 0; i < bl->pack.buffer_size; i++)
                {
                    if (i % 32 == 0)
                        printf("\n");
                    printf("%02X ", bl->pack.buffer[i]);
                }
                printf("\n");
                if (waitting++ > 10)
                {
                    printf("read timeout! exit! at: 0x%08X\n", read_addr);
                    return -2;
                }
            }
        }
        if (read_remain == 0)
        {
            printf("read success!\n");
            break;
        }
        if (sendtry++ >= 3)
        {
            printf("read timeout! exit!\n");
            return -1;
        }
    }
    FILE *f_read = fopen("./read.bin", "wb");
    if (!f_read)
    {
        printf("open file error!\n");
    }

    size_t writeSize = fwrite(firmwareRead, 1, firmware.size, f_read);
    if (writeSize != firmware.size)
    {
        printf("write file error! %d,%d\n", writeSize, firmware.size);
    }
    fclose(f_read);
    uint32_t crc = crc32_cal(firmwareRead, firmware.size, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    printf("read crc: 0x%08X\n", crc);
    if (crc != firmware.FWcrc)
    {
        printf("crc error! read crc: 0x%08X, firmware crc: 0x%08X\n", crc, firmware.FWcrc);
        for (size_t i = 0; i < firmware.size; i++)
        {
            if (firmware_data[i] != firmwareRead[i])
            {
                printf("read error at 0x%08X, [%02X] [%02X]\n", i, firmware_data[i], firmwareRead[i]);
                return -1;
            }
        }
        return -1;
    }

    return 0;
}
int write_fwInfo(bl_info_t *bl)
{
    uint32_t sendtry = 0;
    printf("\nUpdating firmware info...\n");
    while (1)
    {
        bl_pack_send(bl, CMD_W_FW_INFO, 0, (uint8_t *)&firmware, sizeof(firmware));
        int waitting = 0;
        while (1) // wait ack
        {
            // printf("send: [%D]\n", bl->pack.buffer_size);
            // for(int i = 0; i < bl->pack.buffer_size; i++)
            // {
            //     if(i % 32 == 0)
            //         printf("\n");
            //     printf("%02X ", bl->pack.buffer[i]);
            // }
            delay_ms(10);
            int ret = bl_pack_recv(bl);
            if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
            {
                printf("Done!\n\n");
                return 0;
            }
            else if (ret == 0 && bl->pack.cmd1 == CMD_NACK)
            {
                printf("nack! error:%d\n", bl->pack.data_size);
                break;
            }
            else
            {
                // printf("rev: 0x%08X, [%d]", ret, bl->pack.buffer_size);
                // for (int i = 0; i < bl->pack.buffer_size; i++)
                // {
                //     if (i % 32 == 0)
                //         printf("\n");
                //     printf("%02X ", bl->pack.buffer[i]);
                // }
                // printf("\n");
                if (waitting++ > 10)
                {
                    printf("wait ack on write fw info timeout!\n");
                    break;
                }
            }
        }
        if (sendtry++ >= 2)
        {
            printf("write fw info timeout! exit!\n");
            return -1;
        }
    }
    return 0;
}
int reset(bl_info_t *bl)
{
    uint32_t sendtry = 0;
    printf("\nResetting...\n");
    while (1)
    {
        bl_pack_send(bl, CMD_RESET, 0, NULL, 0);
        int waitting = 0;
        while (1) // wait ack
        {
            delay_ms(10);
            int ret = bl_pack_recv(bl);
            if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
            {
                printf("Done!\n\n");
                return 0;
            }
            else if (ret == 0 && bl->pack.cmd1 == CMD_NACK)
            {
                printf("nack! error:%d\n", bl->pack.data_size);
            }
            else
            {
                if (waitting++ > 10)
                {
                    printf("wait ack on reset timeout!\n");
                    break;
                }
            }
        }
    }
    if (sendtry++ >= 2)
    {
        printf("reset timeout! exit!\n");
        return -1;
    }
}
/**
 * @brief
 * make -f ./win_iap_tool/makefile
 * ./win_iap_tool/win_iap_tool.exe COM1 921600 "./ZINO PREMIUM RT DRONE V4.0.0.0.bin"
 * ./win_iap_tool/win_iap_tool.exe COM5 921600 "./ZINO_PREMIUM_ZINO_FC_V4_V1.0.1_20241119.bin"
 * ./win_iap_tool/win_iap_tool.exe COM3 921600 "./ZINO_PREMIUM_ZINO_FC_V4_V1.0.1_20241120.bin"
 * ./win_iap_tool/win_iap_tool.exe COM5 921600 "./ZINO_PREMIUM_ZINO_FC_V4_V1.0.1_20241202.bin"
 * @param argc 4
 * @param argv [com] [baudrate] [firmware]
 * @return int
 */
int main(int argc, char *argv[])
{
    bootloader.read = bl_read;
    bootloader.write = bl_write;
    bootloader.sectorSize = FLASH_SectorSize;

// ffmpeg -i ./wav/happy_new_year.wav ./wav/happyNewYear.sbc -ar 8000
//"ls /dev/tty*"可扫描口列表
// gcc ./AromatherapyDiffuser/hardware/W25QXX/loadSbc.c -o loadsbc
//  printf("system run \n");
#if 0
    struct
    {
        char *cmd;
        char *param[16];
        uint8_t paramCount;
    } cmdlist[16] = {0};
    uint8_t cmdlistCount = 0;
    uint8_t cmdParamCount = 0;
    for (int i = 1; i < argc; i++)
    {
        if (argv[i][0] == '-')
        {
            cmdlist[cmdlistCount].cmd = argv[i];
            cmdlist[cmdlistCount].paramCount = 0;
            cmdParamCount = 0;
            cmdlistCount++;
        }
        else
        {
            if (cmdlistCount != 0)
            {
                cmdlist[cmdlistCount - 1].param[cmdParamCount] = argv[i];
                cmdParamCount++;
                cmdlist[cmdlistCount - 1].paramCount = cmdParamCount;
            }
        }
    }
    printf("cmdlistCount:%d\n", cmdlistCount);
    for (int i = 0; i < cmdlistCount; i++)
    {
        printf("cmdlist[%d]:%s", i, cmdlist[i].cmd);
        if (cmdlist[i].paramCount != 0)
        {
            for (int j = 0; j < cmdlist[i].paramCount; j++)
            {
                printf(" %s", cmdlist[i].param[j]);
            }
        }
        printf("\n");
    }
#endif

    if (argc < 4)
    {
        printf("Usage:%s [com] [baudrate] [firmware]\n", argv[0]);
        printf("Example:\n ./win_iap_tool COM9 921600 \"./ZINO PREMIUM RT DRONE V4.0.0.0.bin\"\n");
        return -1;
    }

    if (argc >= 5)
    {
        SetSize = atoi(argv[4]);
    }
    printf("\n\n * * * * * * ZINO Firmware Burning Tool V2.0.0 * * * * * * \n\n");

    /**serial port init */
    int baudrate = atoi(argv[2]);
    char *com = argv[1];
    if (serial_init(&f_com, com, baudrate) != 0)
    {
        printf("serial init error!\n");
        return -1;
    }

    // 判断文件尾缀是否为.bin
    char *fw_file = argv[3];
    int fw_file_len = strlen(fw_file);
    if (strcmp(fw_file + fw_file_len - 4, ".bin") != 0)
    {
        printf("bin file error! Only accept *.bin file!\n");
        return -1;
    }

    printf("\nbin file:%s\n", fw_file);

    FILE *f_fw = fopen(fw_file, "r");
    if (f_fw == NULL)
    {
        printf("bin file open error! %s\n", strerror(errno));
        return -1;
    }
    printf("firmware extract...\n");
    if (firmware_extract(f_fw, &firmware) != 0)
    {
        printf("firmware extract error!\n");
        return -1;
    }

    size_t sector_size = FLASH_SectorSize;
    size_t read_size = 0;
    size_t total_write = 0;
    size_t total_pack = firmware.size / sector_size + (firmware.size % sector_size ? 1 : 0);
    size_t pack_count = 0;
    uint32_t write_addr = firmware.start_addr;
    uint32_t sector_count = 0;
    // printf("total_pack:%d\n", total_pack);

    timer_t startTime = time(NULL);
    timer_t frameTime = time(NULL);
    timer_t frameTimeUsed = time(NULL);
    timer_t totalTime = time(NULL);
    timer_t totalTimeUsed = time(NULL);

    /**NOTE SYNC */
    if (dev_sync(bl) != 0)
    {
        printf("dev_sync error!\n");
        return -1;
    }
    /**NOTE GET VERSION */
    if (getVersion(bl) != 0)
    {
        printf("dev_get_version error!\n");
        return -1;
    }
    /**NOTE GET ID */
    if (getId(bl) != 0)
    {
        printf("dev_get_id error!\n");
        return -1;
    }

    /**NOTE GET fwInfo */
    if (get_fwInfo(bl) != 0)
    {
        printf("dev_get_fwinfo error!\n");
        return -1;
    }
    /**NOTE ERASE FLASH */
    if (erase_flash(bl) != 0)
    {
        printf("erase_flash error!\n");
        return -1;
    }
    /** NOTE WRITE FLASH */
    if (write_firmware(bl) != 0)
    {
        printf("write_firmware error!\n");
        return -1;
    }
    /** NOTE read FLASH */
    // if(read_firmware(bl) != 0)
    // {
    //     printf("read_firmware error!\n");
    //     return -1;
    // }
    /**NOTE write fwInfo */
    if (write_fwInfo(bl) != 0)
    {
        printf("write_fwInfo error!\n");
        return -1;
    }
    /**NOTE RESET */
    if (reset(bl) != 0)
    {
        printf("reset error!\n");
        return -1;
    }
    tcflush(f_com, TCIOFLUSH); // 刷新输入输出缓冲区
    close(f_com);

    return 0;
}
